package com.cah.project.compare.util;

import com.cah.project.compare.annotation.*;
import com.cah.project.compare.comparator.DefaultComparator;
import com.cah.project.compare.enums.ExceptionEnum;
import com.cah.project.compare.enums.PropertyTypeEnum;
import com.cah.project.compare.exception.CompareException;
import com.cah.project.compare.model.PropertyModel;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * 功能描述: 解析工具 <br/>
 */
public class AnalyzeUtil {

    /** Key连接符 */
    private static final String KEY_LINK = "|_|";

    /**
     * 功能描述: 将List转换成Map，key为List中对象的 唯一标识（@PropertyId注解） <br/>
     *
     * @param cs Collection集合
     * @return "java.util.Map<java.lang.String,java.lang.Object>"
     */
    public static Map<String, Object> toMap(Collection<?> cs) throws IllegalAccessException {
        Map<String, Object> map = new LinkedHashMap<>();
        if(cs != null && !cs.isEmpty()) {
            for(Object o : cs) {
                // 如果是基本类型，则直接toString
                String key = isBaseType(o.getClass().getName()) ? o.toString() : getKey(o);
                map.put(key, o);
            }
        }
        return map;
    }

    /**
     * 功能描述: 将对象转成map，key为属性名，value为对象映射成的PropertyModel模型 <br/>
     *
     * @param o 对象
     * @return "java.util.Map<java.lang.String,com.compare.model.PropertyModel>"
     */
    public static Map<String, PropertyModel> toMap(Object o) throws IllegalAccessException, InstantiationException {
        if(Objects.isNull(o)) {
            return null;
        }
        // 获取全部字段
        Field[] fields = o.getClass().getDeclaredFields();
        List<PropertyModel> list = new ArrayList<>(fields.length);
        String propertyEntity = getPropertyEntity(o);
        String key = getKey(o);
        for (Field field : fields) {
            // 非忽略注解
            if(!isIgnore(field)) {
                field.setAccessible(true);
                PropertyModel pm = new PropertyModel();
                pm.setKey(key);
                // 设置属性信息
                pm.setName(field.getName());
                pm.setValue(field.get(o));
                // 设置类型
                pm.setType(field.getType());
                // 设置属性类型枚举
                if(isBaseType(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.BASE_TYPE);
                } else if(isList(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.LIST_TYPE);
                } else if(isMap(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.MAP_TYPE);
                } else {
                    pm.setPte(PropertyTypeEnum.ENTITY_OBJECT_TYPE);
                }
                PropertyField propertyField = field.getAnnotation(PropertyField.class);
                if(propertyField != null) {
                    pm.setPropertyName(propertyField.name());
                    pm.setOrder(propertyField.order());
                }
                PropertyOrder propertyOrder = field.getAnnotation(PropertyOrder.class);
                if(propertyOrder != null) {
                    // 两个注解都有，哪个大取哪个
                    pm.setOrder(Math.max(pm.getOrder(), propertyOrder.value()));
                }
                // 设置比较器
                PropertyComparator pc = field.getAnnotation(PropertyComparator.class);
                if(pc != null) {
                    pm.setComparator(pc.compare().newInstance());
                } else {
                    if(PropertyTypeEnum.BASE_TYPE.equals(pm.getPte())) {
                        pm.setComparator(new DefaultComparator());
                    }
                }
                pm.setDeclaring(field.getDeclaringClass());
                pm.setPropertyEntity(propertyEntity);
                list.add(pm);
            }
        }
        // 转成有序map
        return list.stream().sorted(Comparator.comparing(PropertyModel::getOrder))
                .collect(Collectors.toMap(PropertyModel::getName, e -> e, throwingMerger(), LinkedHashMap::new));
    }

    /**
     * 功能描述: 异常处理 <br/>
     *
     * @return "java.util.function.BinaryOperator<T>"
     */
    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

    /**
     * 功能描述: 确定唯一标识数据 <br/>
     *
     * @param o 对象
     * @return "java.lang.String"
     */
    public static String getKey(Object o) throws IllegalAccessException {
        if(isBaseType(o.getClass().getName())) {
            return o.toString();
        }
        // 获取全部字段
        Field[] fields = o.getClass().getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        for(Field field : fields) {
            // 非忽略注解
            if(!isIgnore(field)) {
                // 获取@PropertyId注解
                PropertyId id = field.getAnnotation(PropertyId.class);
                if(id != null) {
                    // 校验是否基本类型
                    String typeName = field.getType().getTypeName();
                    checkPropertyIdType(typeName, o);
                    field.setAccessible(true);
                    if(field.get(o) == null || "".equals(field.get(o))) {
                        throw new CompareException(ExceptionEnum.PROPERTY_ID_VALUE_NULL, o.getClass().getName(), field.getName());
                    }
                    sb.append(field.get(o).toString());
                    sb.append(KEY_LINK);
                }
            }
        }
        if(sb.length() <= 0) {
            throw new CompareException(ExceptionEnum.PROPERTY_ID_NULL);
        }
        return sb.substring(0, sb.lastIndexOf(KEY_LINK));
    }

    /**
     * 功能描述: 获取实体标识注解内容 <br/>
     *
     * @param o 对象
     * @return "java.lang.String"
     */
    public static String getPropertyEntity(Object o) {
        return getPropertyEntity(o.getClass());
    }

    /**
     * 功能描述: 获取实体标识注解内容 <br/>
     *
     * @param clazz 类
     * @return "java.lang.String"
     */
    public static String getPropertyEntity(Class<?> clazz) {
        if(isBaseType(clazz.getName())) {
            return "";
        }
        PropertyEntity pe = clazz.getAnnotation(PropertyEntity.class);
        if(pe == null) {
            // 不为基本类型时，必须要有PropertyEntity注解
            throw new CompareException(ExceptionEnum.PROPERTY_ENTITY_NULL);
        }
        return pe.value();
    }

    /**
     * 功能描述: 是忽略注解字段 <br/>
     *
     * @param field 字段
     * @return "boolean" true-是；false-不是
     */
    private static boolean isIgnore(Field field) {
        return field.getAnnotation(PropertyIgnore.class) != null;
    }

    /**
     * 功能描述: 是否列表 <br/>
     * 
     * @param typeName 类型名称
     * @return "boolean"
     */
    private static boolean isList(String typeName) {
        return List.class.getTypeName().equals(typeName);
    }

    /**
     * 功能描述: 是否Map <br/>
     *
     * @param typeName 类型名称
     * @return "boolean"
     */
    private static boolean isMap(String typeName) {
        return Map.class.getTypeName().equals(typeName);
    }

    /**
     * 功能描述: 是否基本类型 <br/>
     *
     * @param className 类
     * @return "boolean" true-是；false-不是
     */
    private static boolean isBaseType(String className) {
        if(className.equals(Integer.class.getName()) ||
                className.equals(int.class.getName()) ||
                className.equals(Byte.class.getName()) ||
                className.equals(byte.class.getName()) ||
                className.equals(Long.class.getName()) ||
                className.equals(long.class.getName()) ||
                className.equals(Double.class.getName()) ||
                className.equals(double.class.getName()) ||
                className.equals(Float.class.getName()) ||
                className.equals(float.class.getName()) ||
                className.equals(Character.class.getName()) ||
                className.equals(char.class.getName()) ||
                className.equals(Short.class.getName()) ||
                className.equals(short.class.getName()) ||
                className.equals(java.math.BigDecimal.class.getName()) ||
                className.equals(java.math.BigInteger.class.getName()) ||
                className.equals(Boolean.class.getName()) ||
                className.equals(boolean.class.getName()) ||
                className.equals(String.class.getName())) {
            return true;
        }
        return false;
    }

    /**
     * 功能描述: 基本类型校验 <br/>
     */
    private static void checkPropertyIdType(String typeName, Object o) {
        if(!"java.lang.String".equals(typeName)
                && !"java.lang.Long".equals(typeName)
                && !"java.lang.Integer".equals(typeName)
                && !"long".equals(typeName)
                && !"int".equals(typeName)) {
            throw new CompareException(ExceptionEnum.PROPERTY_ID_TYPE, o.getClass().getName());
        }
    }

}
